﻿////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// <copyright>Copyright 2008-2011 Andreas Huber Doenni</copyright>
// Distributed under the GNU General Public License version 2 (GPLv2).
// See accompanying file License.txt or copy at http://phuse.codeplex.com/license.
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

namespace Phuse.Net.Nntp
{
    using System;
    using System.Diagnostics.CodeAnalysis;
    using System.IO;
    using System.Text;

    /// <summary>Represents the method that is called when the article must be sent.</summary>
    /// <param name="stream">The stream the article must be written to.</param>
    /// <remarks>The article must be in the MIME format, <strong>without</strong> termination sequence and dot-stuffing.
    /// The final "\r\n.\r\n" sequence as well as the dot-stuffing mandated by
    /// <a href="http://tools.ietf.org/rfc/rfc3977.txt">RFC 3977</a> 3.1.1 is automatically added to the bytes written
    /// to the stream.</remarks>
    public delegate void SendArticleCallback(Stream stream);

    /// <summary>Represents a POST command to be sent to an NNTP server, see
    /// <a href="http://tools.ietf.org/rfc/rfc3977.txt">RFC 3977</a> 6.3.1.</summary>
    /// <threadsafety static="true" instance="false"/>
    public sealed class PostCommand : NonPipelinableCommand<PostResponse>
    {
        private static readonly string[] PostSuccessResponseCodes = { "340" };
        private static readonly string[] ArticleReceivedSuccessResponseCodes = { "240" };

        private readonly SendArticleCallback sendArticleCallback;

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>Initializes a new instance of the <see cref="PostCommand"/> class.</summary>
        /// <param name="sendArticleCallback">The name of the user to authenticate.</param>
        /// <exception cref="ArgumentNullException"><paramref name="sendArticleCallback"/> equals <c>null</c>.
        /// </exception>
        public PostCommand(SendArticleCallback sendArticleCallback)
        {
            if (sendArticleCallback == null)
            {
                throw new ArgumentNullException("sendArticleCallback");
            }

            this.sendArticleCallback = sendArticleCallback;
        }

        ////////////////////////////////////////////////////////////////////////////////////////////////////////////////

        /// <summary>See <see cref="CommandBase{T, U}.SendCommand"/>.</summary>
        protected sealed override void SendCommand(CreateCommandStreamCallback createCommandStreamCallback)
        {
            CommandHelper.WriteSingleLineCommand(createCommandStreamCallback, "POST");
        }

        /// <summary>See <see cref="NonPipelinableCommandBase{T, U}.ReceiveResponse"/>.</summary>
        [SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", Justification = "Not callable from external code, CA bug?")]
        protected sealed override PostResponse ReceiveResponse(
            CreateResponseStreamCallback createResponseStreamCallback,
            CreateCommandStreamCallback createCommandStreamCallback)
        {
            createResponseStreamCallback(Encoding.UTF8, 2, PostSuccessResponseCodes).Dispose();

            using (var commandStream = createCommandStreamCallback(true))
            {
                this.sendArticleCallback(commandStream);
            }

            createResponseStreamCallback(Encoding.UTF8, 2, ArticleReceivedSuccessResponseCodes).Dispose();
            return new PostResponse();
        }
    }
}
